﻿/**************************************************************
** 作   者:  xcc
** 创建时间:  2020-05-21
** 描   述:  一个可以上下拉循环加载数据的listview
           I.   加载数据： 当数据未加载完成 且 滑到底部时，发出加载数据的信号
           II.  切换分类
               a. 当向下拉到顶部，且 超出阈值时，松开手，发出向前信号
               b. 当数据已经加载完成，向上拉到底，超出阈值时，松开手，发出向后信号
           III. 显示提示信息
               a. 没有数据时，提示特定的文本 或 标准的样式 或 指定的样式
               b. 出现加载数据出错时
                   1. 当已经有数据时，恢复到有更多数据时的初始化状态
                   2. 当还没有数据时，根据code值，显示标准的错误提示样式
**************************************************************/
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 1.4

ListView {
    id: viewRoot;
    
    //一页取的数据数量
    property int limit: 12;
    //提前加载-还没到底部时就开始发送加载更多数据信号 
    //提前加载的提前高度
    property int beforehandLoadHt: 300;
    
    //提示内容控件
    property var tipsComponent: null;
    
    signal switchCategory(bool isNex);//请求切换分类
    signal loadMore(); //发出加载更多信号
    
    maximumFlickVelocity: 4500;
    clip: true;
    
    /*!开始加载，会发出加载更多数据信号，并启动loading
     * 适用于初始化状态时调用
     */
    function startLoading(toInit){
        if( viewObj.fState === "loading" )
            return;
        
        if( toInit )
            init();
        
        if( count <=0 )
            viewObj.showCenterLoading();
        
        if( viewObj.fState === "more" || viewObj.fState === "init" )
            Qt.callLater(loadMore);
    }
    
    /*!设置数据到视图
     * 调用时机： 初始加载数据 或 加载更多 数据返回时，调用此方法
     * 描   述:  1. 填充数据到视图中；
                2. 根据返回的数据数量，设置加载状态
                3. 根据返回的code，显示提示信息
     */
    function setDatas(datas,curSize,staJs){
        viewObj.stopCenterLoading();
        if( staJs.code === 0 || staJs.code === -1 )
        {//正确返回
            if( curSize > limit ){//一次性设置超过一页数据时
                if( curSize%limit == 0 )
                    viewObj.fState = "more";
                else
                    viewObj.fState = "noMore";
            }
            else if( curSize === limit )
                viewObj.fState = "more";
            else if( (count+curSize) >= limit )
                viewObj.fState = "noMore";
            else if( (count+curSize) > 0 )
                viewObj.fState = "notLimit";
            else
                viewObj.fState = "noData";
            
            if( !model )
                model = datas;
            
            if( viewObj.fState == "noData" ){
                viewObj.showTips(staJs);
            }
            
            if( viewObj.fState != "more" && viewObj.fState != "init" ){
                bottomSwtItem.state = "ready";
                Qt.callLater(viewObj.refreshSwtState);
            }
            
        }
        else if( count > 0 )
        {//加载失败，但已经取到数据了，
            viewObj.fState = "more";
        }
        else
        {//加载失败，没有取到数据，提示失败原因
            viewObj.fState = "init";
            viewObj.showTips(staJs);
        }
    }
    
    /*!刷新数据完成时，调用此方法，复位刷新状态
     */
    function refreshFinish(){
        viewObj.hState = "refFinish";
    }
    
    /*! 初始化参数，回到初始化状态
     */
    function init(){
        viewObj.topCurHt = 0;
        viewObj.fState = "init";
        
        topSwtItem.state = "init";
        bottomSwtItem.state = "init";
        
        model = null;
        viewObj.hideTips();
    }
    
    QtObject{
        id: viewObj;
        property int topCurHt: 0;
        property int beforehandLoadEndY: (contentHeight-height-beforehandLoadHt);
        property string fState: "init";
        property var centerLoading: null;
        
        /*!没有数据 或 加载数据失败时 的提示
         */
        function showTips(staJs){
            if( !tipsComponent )
                tipsComponent = Qt.createComponent("qrc:/xcqml/tips/MTipsCore.qml");
            tipsLoader.init(staJs);
        }
        
        function hideTips(){
            tipsLoader.sourceComponent = undefined;
        }
        
        /*!刷新顶部及底部切换提示分类状态
        */
        function refreshSwtState(){
            if( contentY < 0 )
            {//刷新顶部切换状态
                if( bottomSwtItem.state !== "init" && bottomSwtItem.state !== "release" )
                    bottomSwtItem.state = "ready";
                
                if( topSwtItem.state != "release" )
                {
                    if( contentY > -topSwtItem.height )
                        topSwtItem.state = "pulling";
                    else 
                        topSwtItem.state = "waitRelease";
                }
            }
            else if( bottomSwtItem.state != "init" )
            {//满足底部显示切换分类的第一个条件: 没有数据了
                if( topSwtItem.state !== "release" )
                    topSwtItem.state = "init";
                
                var bMargin = contentY-(contentHeight-height);
                if( contentHeight <= 0 || contentHeight <= height )
                    bMargin = contentY;
                if( bMargin >= 0 )//刷新底部切换状态
                {
                    bottomSwtItem.anchors.bottomMargin = bMargin;
                    if( bottomSwtItem.state != "release" && !flicking )
                    {
                        if( bMargin < bottomSwtItem.height )
                            bottomSwtItem.state = "pulling";
                        else if( bMargin > bottomSwtItem.height )
                            bottomSwtItem.state = "waitRelease";
                    }
                }
                else if( bottomSwtItem.state === "pulling" ){
                    bottomSwtItem.state = "ready";
                }
            }
            else
            {
                if( topSwtItem.state !== "release" )
                    topSwtItem.state = "init";
                if( bottomSwtItem.state !== "init" && bottomSwtItem.state !== "release" )
                    bottomSwtItem.state = "ready";
            }
        }
        
        /*!检查是否发送切换信号
        */
        function checkSwtich(){
            if( topSwtItem.state === "waitRelease" )
                topSwtItem.state = "release";
            else if( bottomSwtItem.state === "waitRelease")
                bottomSwtItem.state = "release";
            else if( topSwtItem.state === "release" ){
                topSwtItem.state = "init";
                switchCategory(false);
            }
            else if( bottomSwtItem.state === "release" ){
                bottomSwtItem.state = "init";
                switchCategory(true);
            }
        }
        
        /*!检查加载更多
         */
        function checkLoadMore(){
            if( fState === "loading" || contentY <= 0 )
                return;
            
            if( fState != "more" || count <=0 )
                return;
            
            if( atYEnd || (contentY >= beforehandLoadEndY )){
                fState = "loading";
                loadMore();
            }
        }
        
        //启动loading
        function showCenterLoading(){
            if( !centerLoading ){
                centerLoading = cLoadingCpt.createObject(viewRoot,{});
                centerLoading.running = true;
            }
        }
        
        //停止loading
        function stopCenterLoading(){
            if( centerLoading )
            {
                centerLoading.running = false;
                centerLoading.destroy();
                centerLoading = null;
            }
        }
        
    }
    
    footer: moreCpt;
    
    onFlickEnded: viewObj.checkLoadMore();
    onAtYEndChanged: viewObj.checkLoadMore();
    //检查是否触发刷新 
    onDragEnded: viewObj.checkSwtich();
    
    //检查是否触发加载更多
    onMovementEnded: {
        viewObj.refreshSwtState();
        viewObj.checkSwtich();
        viewObj.checkLoadMore();
    }
    
    onContentYChanged: {
        viewObj.refreshSwtState();
        Qt.callLater(viewObj.checkLoadMore);
    }
    
    /*!底部加载更多及提示控件
     */
    Component{
        id: moreCpt;
        Item{
            id: moreCptItem;
            width: viewRoot.width;
            state: viewObj.fState;
            visible: height>0;
            
            Row{
                height: parent.height;
                anchors.horizontalCenter: parent.horizontalCenter;
                spacing: 5;
                
                BusyIndicator{
                    id: moreLoading;
                    width: 20
                    height: width;
                    anchors.verticalCenter: parent.verticalCenter;
                    visible: running;
                    running: false; 
                }
                
                Text {
                    id: moreTips;
                    font.pointSize: 11;
                    anchors.verticalCenter: parent.verticalCenter;
                }
            }
            
            states: [
                State {
                    name: "more"
                    PropertyChanges { target: moreTips; text: "上拉加载更多";}
                    PropertyChanges { target: moreCptItem; height: 30;}
                    PropertyChanges { target: moreLoading; running: false;}
                },
                State {
                    name: "loading"
                    PropertyChanges { target: moreTips; text: "正在加载...";}
                    PropertyChanges { target: moreCptItem; height: 30;}
                    PropertyChanges { target: moreLoading; running: true;}
                },
                State {
                    name: "noMore"
                    PropertyChanges { target: moreTips; text: "";}//没有更多数据
                    PropertyChanges { target: moreCptItem; height: 30;}
                    PropertyChanges { target: moreLoading; running: false;}
                },
                State {
                    name: "notLimit" || "noData" || "init"
                    PropertyChanges { target: moreTips; text: "";}
                    PropertyChanges { target: moreCptItem; height: 0;}
                    PropertyChanges { target: moreLoading; running: false;}
                }
            ]
        }
    }
    
    /*!中心加载转圈提示*/
    Component{
        id: cLoadingCpt;
        BusyIndicator {
            anchors.centerIn: parent
            width: 40;
            height: width;
            anchors.verticalCenterOffset: -parent.height/4;
        }
    }
    
    //下滑查看上一分类 | 释放查看上一分类
    Item{
        id: topSwtItem;
        property bool isThreshold: (contentY<0&&contentY<(-height));//是否到达阀值
        width: parent.width;
        height: 40;
        anchors.bottom: parent.top;
        anchors.bottomMargin: contentY;
        state: "init";
        
        RowLayout{
            anchors.centerIn: parent;
            Image {
                id: topSwtDArrowImg;
                property int ht: 16;
                Layout.preferredHeight: ht;
                Layout.preferredWidth: ht;
                source: "qrc:/image/double_arrow_toB.png"
                Layout.alignment: Qt.AlignVCenter;
            }
            
            Text {
                id: topSwtTipsText;
                font.pointSize: 10;
                Layout.alignment: Qt.AlignVCenter;
            }
        }
        
        states: [
            State {
                name: "init"
                PropertyChanges {target: topSwtDArrowImg; rotation: 0}
                PropertyChanges {target: topSwtTipsText; text: qsTr("下滑查看上一分类")}
                PropertyChanges {target: topSwtItem; visible: false}
            },
            State {
                name: "pulling"
                PropertyChanges {target: topSwtDArrowImg; rotation: 0}
                PropertyChanges {target: topSwtTipsText; text: qsTr("下滑查看上一分类")}
                PropertyChanges {target: topSwtItem; visible: true}
            },
            State {
                name: "waitRelease"
                PropertyChanges {target: topSwtDArrowImg; rotation: 180}
                PropertyChanges {target: topSwtTipsText; text: qsTr("释放查看上一分类")}
                PropertyChanges {target: topSwtItem; visible: true}
            },
            State {
                name: "release"
                PropertyChanges {target: topSwtDArrowImg; rotation: 180}
                PropertyChanges {target: topSwtTipsText; text: qsTr("释放查看上一分类")}
                PropertyChanges {target: topSwtItem; visible: true}
            }
        ]
        
        transitions: [
            Transition {
                NumberAnimation{ property: "rotation"; duration: 200}
            }
        ]
    }
    
    //上滑查看下一分类 | 释放查看下一分类
    Item{
        id: bottomSwtItem;
        width: parent.width;
        height: 40;
        anchors.bottom: parent.bottom;
        state: "init";
        
        RowLayout{
            anchors.centerIn: parent;
            Image {
                id: bottomSwtDArrowImg;
                property int ht: 16;
                Layout.preferredHeight: ht;
                Layout.preferredWidth: ht;
                source: "qrc:/image/double_arrow_toB.png"
                Layout.alignment: Qt.AlignVCenter;
            }
            
            Text {
                id: bottomSwtTipsText;
                font.pointSize: 10;
                Layout.alignment: Qt.AlignVCenter;
            }
        }
        
        states: [
            State {
                name: "init"
                PropertyChanges {target: bottomSwtDArrowImg; rotation: 180}
                PropertyChanges {target: bottomSwtTipsText; text: ""}
                PropertyChanges {target: bottomSwtItem; visible: false}
            },
            State {
                name: "ready"
                PropertyChanges {target: bottomSwtDArrowImg; rotation: 180}
                PropertyChanges {target: bottomSwtTipsText; text: "上滑查看下一分类"}
                PropertyChanges {target: bottomSwtItem; visible: false}
            },
            State {
                name: "pulling"
                PropertyChanges {target: bottomSwtDArrowImg; rotation: 180}
                PropertyChanges {target: bottomSwtTipsText; text: qsTr("上滑查看下一分类")}
                PropertyChanges {target: bottomSwtItem; visible: true}
            },
            State {
                name: "waitRelease";
                PropertyChanges {target: bottomSwtDArrowImg; rotation: 0}
                PropertyChanges {target: bottomSwtTipsText; text: qsTr("释放查看下一分类")}
                PropertyChanges {target: bottomSwtItem; visible: true}
            },
            State {
                name: "release";
                PropertyChanges {target: bottomSwtDArrowImg; rotation: 0}
                PropertyChanges {target: bottomSwtTipsText; text: qsTr("释放查看下一分类")}
                PropertyChanges {target: bottomSwtItem; visible: true}
            }
        ]
        
        transitions: [
            Transition {
                NumberAnimation{ property: "rotation"; duration: 200}
            }
        ]
        
    }
    
    /*!提示加载器*/
    Loader{
        id: tipsLoader;
        property var tipsSta: null;
        anchors.fill: viewRoot;
        parent: viewRoot.contentItem;
        visible: item;
        function init(staJs){
            tipsSta = staJs;
            sourceComponent = tipsComponent;
        }

        onLoaded: {
            item.init(tipsSta);
        }
    }
    
}

